#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <wait.h>

#include "file.h"
#include "http_request.h"

#define BUF_LEN 1024
#define PORT 8000

char base_dir[] = "www";

int request_handle();
void wait_chld(int signum);
int http404();
int static_file(struct http_request *request);
int script_file(struct http_request *request);
int set_cgi_env(struct http_request *request);

int main()
{
	struct sockaddr_in serveraddr, clientaddr;
	socklen_t cliaddr_len = sizeof(clientaddr);

	int serfd, clifd;
	int cnt = 0;

	printf("server start(pid:%d) at port: %d\n", getpid(), PORT);

	signal(SIGCHLD, wait_chld);

	if((serfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
		perror("socket");
		exit(1);
	}

	int reuse = 1;
	if(setsockopt(serfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0){
		perror("setopt");
		close(serfd);
		exit(1);
	}
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
	serveraddr.sin_port = htons(PORT);

	if(bind(serfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) == -1){
		perror("bind");
		close(serfd);
		exit(1);
	}

	if(listen(serfd, 20) == -1){
		perror("listen");
		close(serfd);
		exit(1);
	}


	while(1){
		clifd = accept(serfd, (struct sockaddr*)&clientaddr, \
		&cliaddr_len);

		if(clifd == -1){
			perror("accept");
			continue;
		}

		int pid;

		if((pid = fork()) < 0){
			perror("fork");
			close(clifd);
			continue;
		}

		if(pid){
			//father;
			++cnt;
			close(clifd);
			continue;
		}
		
		//son
		close(serfd);	
		if(dup2(clifd, STDIN_FILENO) == -1){//stdin
			perror("dup2");
		}
		dup2(clifd, STDOUT_FILENO); //stdout
		close(clifd);

		char addrinfo[INET_ADDRSTRLEN];

		inet_ntop(AF_INET, &clientaddr.sin_addr, \
		addrinfo, sizeof(addrinfo));

		fprintf(stderr, "[%d] connetion from %s\n", cnt, addrinfo);
		
		if(request_handle() == 0){
			fprintf(stderr, "[%d] close connection from %s\n", cnt, addrinfo);
			exit(0);
		}
	}

	return 0;
}




void wait_chld(int signum)
{
	int pid;
	while( (pid = waitpid(-1, NULL, WNOHANG)) > 0);
}


int request_handle()
{
	char file_name[128];
	char default_file[] = "/index.html";
	char type[32];
	
	struct http_request request;
	build_request(&request);
	
	if(strcmp(request.file_name, "/") == 0){
		strcpy(request.file_name, default_file);
	}

	sprintf(file_name, "%s%s", base_dir, request.file_name);
	strcpy(request.file_name, file_name);

	fprintf(stderr, "%s %s?%s ", request.method, request.file_name, request.query_string);

	FILE *fp = fopen(file_name, "rb");

	if(fp == NULL){
		fprintf(stderr, "400\n");
		http404();
	}else{
		fprintf(stderr, "200\n");
		get_type(file_name, type);

		if(strcmp(type, "py") == 0){
			set_cgi_env(&request);
			script_file(&request);
		}else{
			static_file(&request);
		}
	}
	
	free_request(&request);
	return 0;
}


int http404()
{
	char *buf;
	char tmp[64];

	sprintf(tmp, "%s/404.html", base_dir);
	FILE *fp = fopen(tmp, "rb");

	if(fp == NULL)
		return 1;
	int len = file_content(fp, &buf);	

	header("HTTP/1.1 404 NOT FOUND");
	header("Content-Type:text/html");
	sprintf(tmp, "Content-Length:%d\r\n", len);
	header(tmp);

	write(STDOUT_FILENO, buf, len);
	free(buf);
	return 0;
}
	
	
int static_file(struct http_request *request)
{
	char tmp[64];
	char *buf;
	FILE *fp;

	fp = fopen(request->file_name, "rb");
	if(fp == NULL)
		return 1;
	int len = file_content(fp, &buf);	

	header("HTTP/1.1 200 OK");
	header("Content-Type:text/html");
	sprintf(tmp, "Content-Length:%d\r\n", len);
	header(tmp);

	write(STDOUT_FILENO, buf, len);
	
	free(buf);
	return 0;
}
	
int script_file(struct http_request *request)
{
	int pid;
	int fd[2];
	char buf[1024];
	int len;

	if(pipe(fd) < 0){
		perror("pipe");
		return 1;
	}

	if((pid = fork()) == -1){
		perror("fork");
		return 1;
	}

	if(pid == 0){
		//son
		dup2(fd[0], STDIN_FILENO);
		close(fd[1]);
		execl("/usr/bin/python", "python", request->file_name, (char*)0);
	}else{
		//father
		len = atoi(request->content_length);
		
		fgets(buf, len + 1, stdin);	
		write(fd[1], buf, len);
		close(fd[1]);
		close(fd[0]);
		wait(0);
	}

	return 0;
}

int set_cgi_env(struct http_request *request)
{
	if(request->query_string != NULL)
		setenv("QUERY_STRING", request->query_string, 1);
	setenv("REQUEST_METHOD", request->method, 1);
	setenv("CONTENT_TYPE", request->content_type, 1);
	setenv("CONTENT_LENGTH", request->content_length, 1);
	return 0;
}


